/*
 * main.cpp
 *
 *  Created on: Feb 5, 2010
 *      Author: Jamie LaPointe
 *       Notes: Gives an example of using a Thread Specific Data (TSD) structure; this allows
 *              the use of a 'shared' data structure whose value is specific to a thread.
 */

#include <pthread.h>
#include <iostream>
#include <math.h>

using std::cout;
using std::endl;

#define THREAD_NUM 5

pthread_key_t house_key; // TSD structure

struct ThreadArg
{
   int threadId;
   float val;
};

void pretendWork()
{
   int a = 1;
   int b = 1;
   double fakeResult = 0;
   while (a < 101)
   {
      while (b < 101)
      {
         fakeResult = (a * b) / (a + b);
         ++a;
         ++b;
      }
   }
}

void bar()
{
   ThreadArg * arg = (ThreadArg *) pthread_getspecific(house_key);
   cout << "Thread id #" << arg->threadId << " val = " << arg->val << endl;
}

void * foo(void * arg)
{
   pthread_setspecific(house_key, arg);
   bar();

   pretendWork();

   pthread_exit(NULL);
   return 0;
}

//
// The 'destructor' function that gets called when a thread exits - this is to clean the TSD data (in
// case there were open file descriptors, malloc'ed data, etc. that needs to be cleaned up.
//
void destroyer(void * arg)
{
   cout << "destroying the ThreadArg 'object' for thread id #" << ((ThreadArg *)arg)->threadId << endl;
}

int main()
{
   ThreadArg threadArg;
   pthread_t threads[THREAD_NUM];
   int index;

   //
   // We will be creating detached threads.
   //
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

   //
   // Create (e.g. initialize) the TSD structure; along with passing along a function
   // pointer to be called when the thread exits --> perform any cleanup here in this
   // 'destructor' function.
   //
   pthread_key_create(&house_key, destroyer);

   //
   // Create our thread creation loop where we set thread specific data.
   //
   for (index = 0; index < THREAD_NUM; ++index)
   {
      threadArg.threadId = index;
      threadArg.val = 3.141592653589 * (index + 1);
      pthread_create(&threads[index], &attr, foo, (void *) &threadArg);
   }

   cout << "Can I print before all threads finish?" << endl;

   pthread_exit(NULL);
   return 0;
}
